
import json
from httprunner import Config, Parameters, Step, RunRequest
import yaml
import os
import shutil


ASSERT_MAP = {
    "eq": "assert_equal",
    "equal": "assert_equal",
    "ne":"assert_not_equal",
    "gt":"assert_greater_than",
    "lt":"assert_less_than",
    "ge":"assert_greater_or_equals",
    "le":"assert_less_or_equals",
    "len_eq":"assert_length_equal",
    "len_gt":"assert_length_greater_than",
    "len_lt":"assert_length_less_than",
    "len_ge":"assert_length_greater_or_equals",
    "len_le":"assert_length_less_or_equals",
    "str_eq":"assert_string_equals",
    "startswith":"assert_startswith",
    "endswith":"assert_endswith",
    "regex_match":"assert_regex_match",
    "contains":"assert_contains",
    "contained_by":"assert_contained_by",
    "type_match":"assert_type_match",
}
def dict_to_list(dic):
    li = []
    for key, value in dic.items():
        if isinstance(value, list):
            li.append({"name": key, "value": json.dumps(value)})
        else:
            li.append({"name": key, "value": value})
    return li


def list_to_dict(li):
    param_dict = {}
    for item in li:
        value = item["value"]
        try:
            value = json.loads(value)
        except Exception as e:
            pass
        try:
            value = eval(value)
        except Exception as e:
            pass
        if isinstance(value, int) or isinstance(value, str) and value.isdigit():
            param_dict[item["name"]] = int(value)
        else:
            param_dict[item["name"]] = value

    return param_dict


def get_config(config):
    """
    获取用例配置对象
    """
    casename = config.get('name')
    base_url = config.get('base_url')
    is_https = config.get('isHttps')
    variables_dict = list_to_dict(config.get('variableList'))

    exports = config.get('export')
    config_obj = (Config(casename).
                  base_url(base_url)
                  .verify(is_https)
                  .variables(**variables_dict)
                  .export(*exports))
    return config_obj


def parse_assert(li):
    assert_list = []
    for item in li:
        funcName = list(item.keys())[0]
        if len(item[funcName])> 2:
            actual, expected, msg = item[funcName]
            assert_list.append({"funcName": funcName, "actual": actual, "expected": expected, "msg": msg})
        else:
            actual, expected = item[funcName]
            assert_list.append({"funcName": funcName, "actual": actual, "expected": expected})
    return assert_list


def format_assert(li):
    assert_list = []
    for item in li:
        assert_func = item["funcName"]
        actual = item["actual"]
        expected = item["expected"]
        msg = item.get("msg")
        if isinstance(expected, str) and expected.isdigit():
            expected = int(expected)
        if msg:
            assert_list.append(
                {assert_func: [actual, expected, msg]}
            )
        else:
            assert_list.append(
                {assert_func: [actual, expected]}
            )
    return assert_list


def parse_yaml(yaml_content):
    case_data = yaml.safe_load(yaml_content)  # 将YAML内容解析为Python对象
    config = case_data.get("config")
    config_keys = config.keys()
    if config:
        public_attr = ["base_url", "export", "name", "verify", "parameters", "variables"]
        custom_attr_list = []
        #自定义属性处理
        for attr in config_keys:
            if attr not in public_attr:
                custom_attr_list.append({"name": attr, "value": config[attr]})
        config["customAttrList"] = custom_attr_list


        # 参数化处理
        parameters = config.get("parameters")
        if parameters:
            config["parameterList"] = dict_to_list(parameters)
            config.pop("parameters")
        else:
            config["parameterList"]  = []
        # 变量处理
        variables = config.get("variables")
        if variables:
            config["variableList"] = dict_to_list(variables)
            config.pop("variables")
        else:
            config["variableList"] = []

        export = config.get("export")
        if export is None:
            config["export"] = []

    # 测试用例处理
    steps = case_data.get("teststeps")
    if steps:
        for step in steps:
            # 请求处理
            cookies = step.get("request").get("cookies")
            if cookies:
                cookie_dict = dict_to_list(cookies)
                step["request"]["cookieList"] = cookie_dict
                step["request"].pop("cookies")
            else:
                step["request"]["cookieList"] = []

            headers = step.get("request").get("headers")
            if headers:
                headers_dict = dict_to_list(headers)
                step["request"]["headerList"] = headers_dict
                step["request"].pop("headers")
            else:
                step["request"]["headerList"] = []

            setup_hooks = step.get("setup_hooks")
            if setup_hooks is None:
                step["setup_hooks"] = []

            teardown_hooks = step.get("teardown_hooks")
            if teardown_hooks is None:
                step["teardown_hooks"] = []

            data_dict = step.get("request").get("data")
            param = step.get("request").get("params")
            json_data = step.get("request").get("json")
            if isinstance(json_data, str):
                json_data = json.loads(json_data)
            if data_dict:
                step["paramRadio"] = 1
                data_list = dict_to_list(data_dict)
                step["request"]["dataList"] = data_list
                step["request"].pop("data")
            else:
                step["request"]["dataList"] = []

            if json_data:
                step["paramRadio"] = 2
                step["request"]["json"] = json.dumps(json_data)
            else:
                step["request"]["json"] = '{}'

            if param:
                param_list = dict_to_list(param)
                step["request"]["paramList"] = param_list
                step["request"].pop("param")
                step["paramRadio"] = 3
            else:
                step["request"]["paramList"] = []


            
            #参数处理
            variable = step.get("variables")
            if variable:
                step["variableList"] = dict_to_list(variable)
                step.pop("variables")
            else:
                step["variableList"] = []

            # 提取处理
            extract = step.get("extract")
            if extract:
                step["extractList"] = dict_to_list(step.get("extract"))
                step.pop("extract")
            else:
                step["extractList"] = []

            # 断言处理
            validate = step.get("validate")
            if validate:
                step["validateList"] = parse_assert(step.get("validate"))
                step.pop("validate")
            else:
                step["validateList"] = []
    return case_data


def write_yaml(config_dict, steps, yaml_path, file_name):
    

    def remove_empty_values_recursively(d):
        if isinstance(d, dict):
            return {k: remove_empty_values_recursively(v) for k, v in d.items() if v not in (None, '', [], {}, '') or (isinstance(v, str) and v.strip())}
        elif isinstance(d, list):
            return [remove_empty_values_recursively(x) for x in d if x not in (None, '', [], {}, '') or (isinstance(x, str) and x.strip())]
        else:
            return d
        
    yaml_dict = {}
    
    custom_attr_list = config_dict.get("customAttrList")
    attr_dict = list_to_dict(custom_attr_list)
    config_dict.update(attr_dict)
    config_dict.pop("customAttrList")

    parameter_list = config_dict.get("parameterList")
    config_dict["parameters"] = list_to_dict(parameter_list)
    config_dict.pop("parameterList")

    variable_list = config_dict.get("variableList")
    config_dict["variables"] = list_to_dict(variable_list)
    config_dict.pop("variableList")

    config_dict = remove_empty_values_recursively(config_dict)
    yaml_dict["config"] = config_dict
    step_list = []
    for step in steps:
        step_dict = {}
        step_dict["name"] = step["name"]
        step_dict["setup_hooks"] = step["setup_hooks"]

        step_dict["variables"] = list_to_dict(step["variableList"])
        
        step_dict["request"] = {}
        step_dict["request"]["method"] = step["request"]["method"]
        step_dict["request"]["url"] = step["request"]["url"]
        step_dict["request"]["headers"] = list_to_dict(step["request"]["headerList"])
        step_dict["request"]["cookies"] = list_to_dict(step["request"]["cookieList"])

        if step["paramRadio"] == 1:
            step_dict["request"]["data"] = list_to_dict(step["request"]["dataList"])
            
        elif step["paramRadio"] == 2:
            step_dict["request"]["json"] = json.loads(step["request"]["json"])
        else:
            step_dict["request"]["params"] = list_to_dict(step["request"]["paramList"])
        
        step_dict["teardown_hooks"] = step["teardown_hooks"]
        step_dict["extract"] = list_to_dict(step["extractList"])
        step_dict["validate"] = format_assert(step["validateList"])
        step_dict = remove_empty_values_recursively(step_dict)
        step_list.append(step_dict)

    yaml_dict["teststeps"] = step_list
    yaml_path = os.path.normpath(yaml_path)
    config_file_path = os.path.join(yaml_path, file_name)
    with open(config_file_path, 'w', encoding="utf8") as file:
        yaml.safe_dump(yaml_dict, file, allow_unicode=True, sort_keys=False)
    
    return os.path.normpath(config_file_path)


def parse_step(step):
    """
    解析可运行步骤
    """
    casename = step.get("name")
    
    param_type = step.get("paramRadio")

    # 生成Step
    template = "Step( \n"
    template += f"RunRequest('{casename}')\n"
    cookie_temp = ""
    header_temp = ""

    # 生成setup
    setup_hooks = step.get("setup_hooks")
    if setup_hooks:
        for setup in setup_hooks:
            template += f'.setup_hook("{setup}")\n'

    # 生成变量
    variables_dict = list_to_dict(step["variableList"])
    if variables_dict:
        template += f'.with_variables(**{variables_dict})\n'

    # 生成请求
    url = step["request"]["url"]
    method = step["request"]["method"]
    method_temp = f".{method.lower()}('{url}')\n"
    template += method_temp
    
    # 生成teardown
    teardown_hooks = step.get("request").get("teardown_hooks")
    if teardown_hooks:
        for teardown in teardown_hooks:
            template += f'.teardown_hook("{teardown}")\n'

    # 生成cookie
    cookie_list = step.get('cookieList')
    if cookie_list:
        cookies_dict = list_to_dict(cookie_list)
        cookie_temp = f'.with_cookies(**{cookies_dict})\n'
    template += cookie_temp

    # 生成header
    headers = step.get("request").get('headerList')
    if headers:
        headers_dict = list_to_dict(headers)
        header_temp = f'.with_headers(**{headers_dict})\n'
    template += header_temp

    # 生成请求参数
    request_data_temp = ""
    if param_type == 1:
        data_list = step["request"]["dataList"]
        from_data_dict = list_to_dict(data_list)
        request_data_temp = f'.with_data({from_data_dict})\n'
    elif param_type == 2:
        json_data = step["request"]["json"].replace("false", "False").replace("true", "True")
        request_data_temp = f".with_json({json_data})\n"
    elif param_type == 3:
        param_list = step["request"]["paramList"]
        param_data_dict = list_to_dict(param_list)
        request_data_temp = f'.with_params(**{param_data_dict})\n'
    
    template += request_data_temp

    # 生成提取变量
    template  += ".extract()\n"
    extract_data_temp = ""
    extract_list = step.get('extractList')
    for extract in extract_list:
        data_temp = f'.with_jmespath("{extract["value"]}", "{extract["name"]}")'
        extract_data_temp += data_temp + "\n"
     
    template += extract_data_temp


    # 生成断言
    template += ".validate()\n"
    validate_list = step.get('validateList')
    validate_data_temp = ""
    for validate in validate_list:
        actual = validate["actual"]
        expected = validate["expected"]
        if isinstance(validate["actual"], int) or validate["actual"].isdigit():
            actual = int(validate["actual"])
        else:
            actual = f'"{actual}"'
        if isinstance(validate["expected"], int) or validate["expected"].isdigit():
            expected = int(validate["expected"])
        else:
            expected = f'"{expected}"'
        msg = validate.get("msg")
        func_key = validate["funcName"]
        assert_func = ASSERT_MAP[func_key]
        if msg:
            data_temp = f".{assert_func}({actual}, {expected}, '{validate['msg']}')"
        else:
            data_temp = f".{assert_func}({actual}, {expected})"
        validate_data_temp += data_temp + "\n"
    template += validate_data_temp

    #结尾
    template += ")"
    return eval(template)



def remove_directory(path):
    if os.path.exists(path):
        shutil.rmtree(path)


def get_all_files(directory):
    all_files = []
    for root, dirs, files in os.walk(directory):
        for file in files:
            all_files.append(os.path.join(root, file))
    return all_files

def get_runcase_log(path):
    if os.path.exists(path):
        logs = ""
        files = get_all_files(path)
        for index, file in enumerate(files):
            with open(file, 'r') as f:  # 'r' 表示读取模式
                logs += f"\n{'-' * 20}case run count{index+1}{'-'*20}\n"
                logs += f.read()  # 读取文件全部内容
        return logs
    return ""
        